package org.springframework.boot.netty.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.netty.NettyProperties;
import org.springframework.boot.netty.annotation.HttpEndPoint;
import org.springframework.boot.netty.annotation.NettySockerHandler;
import org.springframework.boot.netty.annotation.NettySocketListener;
import org.springframework.boot.netty.annotation.RequestMapping;
import org.springframework.boot.netty.argumentResolver.HandlerMethodArgumentResolver;
import org.springframework.boot.netty.argumentResolver.HandlerMethodArgumentResolverComposite;
import org.springframework.boot.netty.enums.MessageEnum;
import org.springframework.boot.netty.exception.ThreadException;
import org.springframework.boot.netty.sync.AsyncMessageProcessing;
import org.springframework.boot.netty.sync.MessageBlockQueue;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;


/**
 * Author: huoxingzhi
 * Date: 2020/12/10
 * Email: hxz_798561819@163.com
 */


public class HttpEndPontPostProcess implements BeanPostProcessor {

    private final static Logger logger = LoggerFactory.getLogger(HttpEndPontPostProcess.class);


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        // find targetClass class from an AOP proxy
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        HttpEndPoint annotation = AnnotationUtils.findAnnotation(targetClass, HttpEndPoint.class);
        if(!ObjectUtils.isEmpty(annotation)){
            mappingMVC(targetClass,bean);
        }

        return bean;
    }



    /*
     * MVC 注解和路径扫描
     */
    private void mappingMVC(Class<?> targetClass,Object bean) {

        RequestMapping requestMappingClass = targetClass.getAnnotation(RequestMapping.class);
        String classPath = "";
        if(!ObjectUtils.isEmpty(requestMappingClass)){
            classPath = requestMappingClass.value();
        }

        String finalClassPath = classPath;
        ReflectionUtils.doWithMethods(targetClass, method -> {
            RequestMapping requestMappingMethod = AnnotationUtils.findAnnotation(method, RequestMapping.class);
            if(requestMappingMethod!=null){

                String methodPath = requestMappingMethod.value();
                String requestPath = finalClassPath + methodPath;
                Method convertedMethod = checkProxy(method, bean);
                HandlerMappingAdapter.getHandleMapping().put(requestPath,convertedMethod);
                HandlerMappingAdapter.getControllerMapping().put(requestPath,bean);
            }
        }, ReflectionUtils.USER_DECLARED_METHODS);

    }



    private Method checkProxy(Method methodArg, Object bean) {
        Method method = methodArg;
        if (AopUtils.isJdkDynamicProxy(bean)) {
            try {
                // Found a @RequestMapping method on the target class for this JDK proxy ->
                // is it also present on the proxy itself?
                method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
                Class<?>[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces();
                for (Class<?> iface : proxiedInterfaces) {
                    try {
                        method = iface.getMethod(method.getName(), method.getParameterTypes());
                        break;
                    }
                    catch (@SuppressWarnings("unused") NoSuchMethodException noMethod) {
                    }
                }
            }
            catch (SecurityException ex) {
                ReflectionUtils.handleReflectionException(ex);
            }
            catch (NoSuchMethodException ex) {
                throw new IllegalStateException(String.format(
                        "@RequestMapping method '%s' found on bean target class '%s', " +
                                "but not found in any interface(s) for a bean JDK proxy. Either " +
                                "pull the method up to an interface or switch to subclass (CGLIB) " +
                                "proxies by setting proxy-target-class/proxyTargetClass " +
                                "attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()), ex);
            }
        }
        return method;
    }


}
